#ifndef __TOKEN_HELPER__
#define __TOKEN_HELPER__

#define ISHEXCHAR( a )		( ( tolower(a) >= 'a' ) && ( tolower(a) <= 'f' ) )

namespace xm
{
	static bool hex_to_4bit( char ch, int & c )
	{
		if( ch >= 'a' && ch <= 'f' ) c = 0xA + ch - 'a';
		else if( ch >= 'A' && ch <= 'F' ) c = 0xA + ch - 'A';
		else if( ch >= '0' && ch <= '9' ) c = ch - '0';
		else return false;
		return true;
	}
	static bool hex_to_buffer( std::string & inString, std::string & outString )
	{
		LPCSTR lpcString = inString.c_str();
		SIZE_T ccString = inString.size();
		if( ccString % 2 ) return false;

		UINT8 b8 = 0;
		for( SIZE_T i = 0; i < ccString; i += 2 )
		{
			int h4v = 0, l4v = 0;
			if( !hex_to_4bit( lpcString[i], h4v ) ) return false;
			if( !hex_to_4bit( lpcString[i+1], l4v ) ) return false;
			b8 = ( h4v*16+l4v);
			outString.push_back( (CHAR)b8 );
		}
		return true;
	}

	static bool hex_to_value( LPCSTR hex, SIZE_T c, INT64 & i64 )
	{
		if( c > 16 ) return false;
		i64 = 0;
		for( SIZE_T i = 0; i < c; i += 2 )
		{
			int h4v = 0, l4v = 0;
			if( !hex_to_4bit( hex[i], h4v ) ) return false;
			if( !hex_to_4bit( hex[i+1], l4v ) ) return false;
			i64 <<= 8;
			i64 += ( h4v*16+l4v);
		}
		return true;
	}

	static bool fetch( LPCSTR & p, LPCSTR pe, char * pout, SIZE_T c )
	{
		if( pe - p < c ) return false;
		memcpy( pout, p, c );
		p += c;
		return true;
	}
	static bool peek( LPCSTR p, LPCSTR pe, char * pout, SIZE_T c )
	{
		if( pe - p < c ) return false;
		memcpy( pout, p, c );
		return true;
	}

	static bool wcs2bin( std::wstring& obin, std::string& ins )
	{
		std::string& ptn = ins;

		LPCSTR ps = ptn.c_str();
		LPCSTR pe = ps + ptn.size();
		LPCSTR p = ps;
		for( ; p < pe; ++ p )
		{
			wchar_t ch = 0;
			int ir = mbtowc(&ch, p, pe-p);
			if( ir <= 0 ) 
			{
				return false;
			}
			if( ir == 2 )
			{
				obin.push_back(ch);
				++ p ;
				continue;
			}

			ch = (wchar_t)*p;
			if( ch == '\\' )
			{
				++ p;
				if( p >= pe ) 
				{
					return false;
				}
				ch = (WORD)*p;

				if( ch == 'x' )
				{
					++p;
					char hex[4] = {};
					if( !fetch( p, pe, hex, sizeof(hex) ) )
					{
						return false; 
					}
					p --; // it'll be ++
					__int64 i64 = 0;
					if( !hex_to_value( hex, 4, i64 ) )
					{
						return false;
					}
					ch = (wchar_t)i64;
				}
				else
				{
					switch(ch)
					{
					case 'a':	ch = '\a';	break;
					case 'b':	ch = '\b';	break;
					case 'f':	ch = '\f';	break;
					case 'n':	ch = '\n';	break;
					case 'r':	ch = '\r';	break;
					case 't':	ch = '\t';	break;
					case 'v':	ch = '\v';	break;
					case 'e':	ch = 27;	break;
					default:
						break;
					}
				}
			}
			if( ch == '\\' ) 
				obin.push_back( (char)'\\' );
			obin.push_back( ch );
		}
		return true;
	}
	
	static bool mbs2bin( std::string& obin, std::string& ins )
	{
		std::string ptn = ins;

		LPCSTR ps = ptn.c_str();
		LPCSTR pe = ps + ptn.size();
		LPCSTR p = ps;
		for( ; p < pe; ++ p )
		{
			char ch = *p;
			if( ch == '\\' )
			{
				++ p;
				if( p >= pe ) 
				{
					return false;
				}
				ch = *p;
				int hex = 0;
				if( ch == 'x' )
				{
					++p;
					char hex[2] = {};
					if( !fetch( p, pe, hex, 2 ) )
					{
						return false;
					}
					p --; // it'll be ++
					__int64 i64 = 0;
					if( !hex_to_value( hex, 2, i64 ) )
					{
						return false;
					}
					ch = (char)i64;
				}
				else
				{
					switch(ch)
					{
					case 'a':	ch = '\a';	break;
					case 'b':	ch = '\b';	break;
					case 'f':	ch = '\f';	break;
					case 'n':	ch = '\n';	break;
					case 'r':	ch = '\r';	break;
					case 't':	ch = '\t';	break;
					case 'v':	ch = '\v';	break;
					case 'e':	ch = 27;	break;
					default:
						break;
					}
				}
			}
			obin.push_back( ch );
			if( ch == '\\' )
				obin.push_back( ch );
		}
		return true;
	}

	static bool is_hex_str( LPCSTR str, SIZE_T len )
	{
		for( SIZE_T i = 0; i < len; ++ i )
			if( !isdigit(str[i]) && !ISHEXCHAR(str[i]) )
				return false;
		return true;
	}

	static bool hex_to_number( LPCSTR p, LPCSTR pe, SIZE_T & s )
	{
		LPCSTR p1 = p;
		for( ; p1 < pe; ++ p1 ) 
			if( !isdigit(*p1) && !ISHEXCHAR(*p1) )
				return false;
		int c = _snscanf( p, pe-p, "%x", &s );
		if( c != 1 ) return false;
		return true;
	}
	static bool hex_to_number( LPCSTR p, int & s )
	{
		LPCSTR p1 = p;
		for( ; *p1; ++ p1 ) 
			if( !isdigit(*p1) && !ISHEXCHAR(*p1) )
				return false;
		int c = sscanf( p, "%x", &s );
		if( c != 1 ) return false;
		return true;
	}
};

#endif